Curso de ScriptVox Intermedirio - Aula 11 - Prof. Oswaldo Vernet - iNCE/UFRJ

Vamos resolver o exerccio anterior, j que apenas uma resposta foi enviada.

Tratemos, primeiramente, da leitura do arquivo "AGENDA.TXT", que tem a
seguinte especificao: duas linhas para cada pessoa, a primeira contendo o
nome, a seguinte contendo o telefone. A ideia  guardar os nomes numa lista
de nomes e os telefones numa lista de telefones, de modo que as duas listas
estejam emparelhadas: o nome armazenado na posio "p" da lista de nomes
corresponde ao telefone armazenado na posio "p" da lista de telefones.

A funo le_arquivo  a seguinte:

* incio da funo le_arquivo
funo le_arquivo : nome, telefone, erro
	abre #1 "AGENDA.TXT"
	nomes := []
	telefones := []
	le #1 MAIUSC nome
	checa erro
	enquanto erro = 0 
		le #1 MAIUSC telefone
		checa erro
		se erro <> 0
			escreve "O arquivo AGENDA.TXT acabou inesperadamente"
			desvia @fim
		fim se
		nomes[] := nome
		telefones[] := telefone
		le #1 MAIUSC nome
		checa erro
	fim enquanto
@fim
	fecha #1
fim funo
* fim da funo le_arquivo

Alguns pontos a observar na funo le_arquivo: 

1. Ela no recebe parmetros, j que o arquivo contendo a agenda foi especificado
   como "AGENDA.TXT". Se este nome pudesse variar (por exemplo, se ele fosse lido do
   teclado), poderamos pensar em passar o nome lido como parmetro. Mas, no caso,
   no  necessrio, pois o nome  fixo.
   
2. As variveis nome, telefone e erro so locais. Elas s precisam existir enquanto
   a funo estiver ativa, lendo o arquivo. Acabada a leitura, no interessa mais
   manter essas variveis. As variveis nomes e telefones so globais, porque sero 
   usadas em outros pontos do script. 
   
3. A leitura do arquivo se d normalmente, lembrando que cada pessoa ocupa duas linhas.
   Portanto, o arquivo, se estiver ntegro, ter um nmero par de linhas. Mesmo
   assim, por questo de segurana, quando lemos o telefone, devemos verificar se
   a leitura ocorreu com sucesso, pois, por algum erro de edio, pode ser que
   exatamente o telefone da ltima pessoa esteja faltando.
   
4. O nome e o telefone de uma pessoa so lidos para as variveis nome e telefone,
   respectivamente. Somente se a leitura no apresentar problemas, o nome  anexado 
   lista nomes e o telefone  anexado  lista telefones. Observe que as duas listas
   so inicializadas como vazias imediatamente antes de a leitura comear.
   
5. O modificador MAIUSC  usado nos comandos de leitura, de forma que os dados
   guardados nas listas estaro sempre em maisculas.

Vamos, agora, ao programa principal. A primeira ao a ser tomada  chamar a funo
le_arquivo. Concluda a leitura, chamamos a funo processa_comandos, que ser responsvel
por interagir com o usurio e fazer as pesquisas na agenda. Portanto, nosso programa
principal fica assim:

* incio do programa principal
le_arquivo ()
processa_comandos ()
termina
* fim do programa principal

H um detalhe importante aqui. A agenda  representada, no script, por duas listas,
guardadas nas variveis nomes e telefones. Estas variveis so globais, porque so
necessrias tanto na funo le_arquivo quanto na funo processa_comandos. Apenas para destacar o
fato de elas serem globais, vamos mover a inicializao das duas variveis, que  feita
na funo le_arquivo, para o programa principal. Assim, para quem est lendo o script, 
ficar logo claro que estas variveis so globais. Juntando as peas, nosso script, 
at agora, fica assim:

* incio do script
* incio do programa principal
nomes := []
telefones := []

le_arquivo ()
processa_comandos ()
termina
* fim do programa principal

* incio da funo le_arquivo   
funo le_arquivo : nome, telefone, erro
	abre #1 "AGENDA.TXT"
	le #1 MAIUSC nome
	checa erro
	enquanto erro = 0 
		le #1 MAIUSC telefone
		checa erro
		se erro <> 0
			escreve "O arquivo AGENDA.TXT acabou inesperadamente"
			desvia @fim
		fim se
		nomes[] := nome
		telefones[] := telefone
		le #1 MAIUSC nome
		checa erro
	fim enquanto
@fim
	fecha #1
fim funo
* fim da funo le_arquivo

* funo processa_comandos
funo processa_comandos
* a completar
fim funo
* fim da funo processa_comandos
* fim do script

O comando TERMINA, ao final do programa principal, no  necessrio. Ele foi introduzido
apenas por clareza.

A funo processa_comandos dever ler o que o usurio deseja pesquisar e realizar a pesquisa,
informando os resultados encontrados. Seu "esqueleto"  o seguinte:

* incio da funo processa_comandos
funo processa_comandos
	escreve "Dado a pesquisar: " &
	le MAIUSC dado
	enquanto dado <> ""
		pesquisa ()
		escreve "Dado a pesquisar: " &
		le MAIUSC dado
	fim enquanto
fim funo
* fim da funo processa_comandos

O enunciado do exerccio no especificava como a interao com o usurio deveria ser
feita. Optamos ento por ficar perguntando ao usurio o que ele deseja pesquisar at
que ele tecle simplesmente nter, encerrando o uso da agenda. O dado a ser pesquisado,
lido em maisculas,  armazenado na varivel "dado". Para cada dado lido, a funo
"pesquisa" (que ser a prxima a implementar)  chamada, com a finalidade de realizar
a pesquisa na agenda. 

Temos duas opes para o escopo da varivel "dado": podemos deix-la como global,
j que ela  usada na funo processa_comandos e tambm ser usada na funo "pesquisa";
ou podemos declar-la como varivel local  funo processa_comandos. Mas a, a funo
pesquisa no teria acesso a ela, pois nenhuma funo tem acesso diretamente s variveis
locais de outra funo. A soluo seria pasar "dado" como parmetro para a funo
"pesquisa". Vamos implementar esta segunda ideia. A nova funo processa_comandos ficaria
assim:

* incio da funo processa_comandos
funo processa_comandos : dado
	escreve "Dado a pesquisar: " &
	le MAIUSC dado
	enquanto dado <> ""
		pesquisa (dado)
		escreve "Dado a pesquisar: " &
		le MAIUSC dado
	fim enquanto
fim funo
* fim da funo processa_comandos

Chegamos, ento, ao ponto central do script: as consultas  agenda. A funo pesquisa
 responsvel por isto. Vamos desenvolv-la por partes, como sugere o enunciado do
exerccio.

A primeira verso far uma pesquisa simples: a cadeia que o usurio digitou est armazenada na
varivel "dado" e dever ser pesquisada ou na lista dos nomes ou na lista dos telefones. 
Como vamos decidir em qual lista ser feita a consulta?

 simples: telefones brasileiros, em geral, contm apenas nmeros. Podemos aproveitar este
fato e fazer um teste: se o que o usurio digitou comea por nmero, assumimos que se trata de
um telefone; caso contrrio, deve tratar-se de um nome. Podemos usar o operador *=* para
testar se o primeiro caractere da cadeia armazenada na varivel "dado"  um dgito da seguinte
forma:

se "0123456789" *=* dado[1]
* o primeiro caractere de dado  um dgito
seno
* o primeiro caractere de dado no  um dgito
fim se

Se o teste for verdadeiro, devemos procurar "dado" na lista de telefones; se for falso,
devemos procurar "dado" na lista de nomes. A funo POS nos ajuda nesta tarefa:

se "0123456789" *=* dado[1]
	posio := POS (dado, telefones)
seno
	posio := POS (dado, nomes)
fim se

Se a pesquisa for bem sucedida, a varivel "posio" conter exatamente a posio
em uma das listas onde "dado" foi encontrado. Se a pesquisa falhar, "posio" ter
o valor menos um e, a, saberemos que o dado digitado no se encontra na agenda.
Nossa funo "pesquisa" fica assim:

* incio da funo pesquisa
funo pesquisa (dado) : posio
	se "0123456789" *=* dado[1]
		posio := POS (dado, telefones)
	seno
		posio := POS (dado, nomes)
	fim se
	se posio < 0
		escreve dado " NO foi encontrado na agenda"
	seno
		escreve "NOME: " nomes[posio]
		escreve "TELEFONE: " telefones[posio]
	fim se
* fim da funo pesquisa

A varivel "posio" foi escolhida como local, j que seu valor no interessa a nenhum
outro ponto do script.

Juntando as peas at agora, o script tem o seguinte aspecto:

* incio do script
* incio do programa principal
nomes := []
telefones := []

le_arquivo ()
processa_comandos ()
termina
* fim do programa principal

* incio da funo le_arquivo   
funo le_arquivo : nome, telefone, erro
	abre #1 "AGENDA.TXT"
	le #1 MAIUSC nome
	checa erro
	enquanto erro = 0 
		le #1 MAIUSC telefone
		checa erro
		se erro <> 0
			escreve "O arquivo AGENDA.TXT acabou inesperadamente"
			desvia @fim
		fim se
		nomes[] := nome
		telefones[] := telefone
		le #1 MAIUSC nome
		checa erro
	fim enquanto
@fim
	fecha #1
fim funo
* fim da funo le_arquivo

* incio da funo processa_comandos
funo processa_comandos : dado
	escreve "Dado a pesquisar: " &
	le MAIUSC dado
	enquanto dado <> ""
		pesquisa (dado)
		escreve "Dado a pesquisar: " &
		le MAIUSC dado
	fim enquanto
fim funo
* fim da funo processa_comandos

* incio da funo pesquisa
funo pesquisa (dado) : posio
	se "0123456789" *=* dado[1]
		posio := POS (dado, telefones)
	seno
		posio := POS (dado, nomes)
	fim se
	se posio < 0
		escreve dado " NO foi encontrado na agenda"
	seno
		escreve "NOME: " nomes[posio]
		escreve "TELEFONE: " telefones[posio]
	fim se
fim funo
* fim da funo pesquisa
* fim do script 
 
Esta primeira verso atende ao que foi pedido na primeira parte do enunciado. Em continuao,
 pedido que repeties de nomes no sejam permitidas na agenda. Isto pode ser detectado
no momento da leitura, da seguinte maneira: para cada nome e telefone lidos, antes de anex-los
s listas, testamos se o nome j no pertence  lista de nomes. Se pertencer, a funo POS 
retornar um nmero maior ou igual a zero e, neste caso, emitimos uma advertncia e no 
fazemos as incluses; se no pertencer, a funo POS retornar menos um e os dados podero
ser includos normalmente. A nova funo le_arquivo fica assim:

* incio da funo le_arquivo   
funo le_arquivo : nome, telefone, erro
	abre #1 "AGENDA.TXT"
	le #1 MAIUSC nome
	checa erro
	enquanto erro = 0 
		le #1 MAIUSC telefone
		checa erro
		se erro <> 0
			escreve "O arquivo AGENDA.TXT acabou inesperadamente"
			desvia @fim
		fim se
		se POS (nome, nomes) >= 0
			escreve "O nome " nome " est repetido no arquivo"
		seno 
			nomes[] := nome
			telefones[] := telefone
		fim se
		le #1 MAIUSC nome
		checa erro
	fim enquanto
@fim
	fecha #1
fim funo
* fim da funo le_arquivo

A ltima melhoria  um pouco mais trabalhosa. Agora, o usurio pode digitar apenas um pedao
do nome e o script dever informar todos os nomes (juntamente com os telefones) em que
o pedao digitado pelo usurio aparecem.

A funo POS, agora, no nos ajuda diretamente.  preciso caminhar pela lista de nomes e,
para cada nome que esteja na lista, devemos verificar se "dado"  uma subcadeia do nome.
O seguinte trecho implementa esta ideia:

	repete n TAMANHO(nomes)
		posio := n - 1
		se POS (dado, nomes[posio]) > 0
			escreve "NOME: " nomes[posio]
			escreve "TELEFONE: " telefones[posio]
		fim se
	fim repete
		
Observe que:

1. A varivel de controle "n" assume os valores 1, 2, ..., sucessivamente, at TAMANHO(nomes),
   que  a cardinalidade da lista armazenada em nomes. Porm, as posies na lista comeam de 0
   e no de 1. Desta maneira, no podemos usar diretamente o valor de "n" para indexar a lista.
   Devemos usar o valor de "n" menos um, que  exatamente o valor atribudo  varivel "posio".
   
2. A funo POS verifica se "dado"  uma subcadeia do nome armazenado na posio "posio" da
   lista. Se for, nome e telefone so impressos na tela.
   
A nova verso da funo "pesquisa" fica assim: 

* incio da funo pesquisa
funo pesquisa (dado) : posio, n
	se "0123456789" *=* dado[1]
		posio := POS (dado, telefones)
		se posio < 0
			escreve dado " NO foi encontrado na agenda"
		seno
			escreve "NOME: " nomes[posio]
			escreve "TELEFONE: " telefones[posio]
		fim se
	seno
		repete n TAMANHO(nomes)
			posio := n - 1
			se POS (dado, nomes[posio]) > 0
				escreve "NOME: " nomes[posio]
				escreve "TELEFONE: " telefones[posio]
			fim se
		fim repete
	fim se
fim funo
* fim da funo pesquisa

e a verso final do script, agora completo,  a seguinte:

* incio do script
* programa principal
nomes := []
telefones := []

le_arquivo ()
processa_comandos ()
termina

* funo le_arquivo   
funo le_arquivo : nome, telefone, erro
	abre #1 "AGENDA.TXT"
	le #1 MAIUSC nome
	checa erro
	enquanto erro = 0 
		le #1 MAIUSC telefone
		checa erro
		se erro <> 0
			escreve "O arquivo AGENDA.TXT acabou inesperadamente"
			desvia @fim
		fim se
		se POS (nome, nomes) >= 0
			escreve "O nome " nome " est repetido no arquivo"
		seno 
			nomes[] := nome
			telefones[] := telefone
		fim se
		le #1 MAIUSC nome
		checa erro
	fim enquanto
@fim
	fecha #1
fim funo
* fim da funo le_arquivo

* incio da funo processa_comandos
funo processa_comandos : dado
	escreve "Dado a pesquisar: " &
	le MAIUSC dado
	enquanto dado <> ""
		pesquisa (dado)
		escreve "Dado a pesquisar: " &
		le MAIUSC dado
	fim enquanto
fim funo
* fim da funo processa_comandos

* incio da funo pesquisa
funo pesquisa (dado) : posio, n
	se "0123456789" *=* dado[1]
		posio := POS (dado, telefones)
		se posio < 0
			escreve dado " NO foi encontrado na agenda"
		seno
			escreve "NOME: " nomes[posio]
			escreve "TELEFONE: " telefones[posio]
		fim se
	seno
		repete n TAMANHO(nomes)
			posio := n - 1
			se POS (dado, nomes[posio]) > 0
				escreve "NOME: " nomes[posio]
				escreve "TELEFONE: " telefones[posio]
			fim se
		fim repete
	fim se
fim funo
* fim da funo pesquisa
* fim do script 
  

Exerccio de avaliao (enviar para scriptvox@gmail.com at o meio-dia de 27 de fevereiro de 2012)

Falta um pequeno detalhe nesta ltima verso do script. Quando o pedao de nome digitado pelo
usurio no casa com nenhum nome da lista, nada  dito. Modifique a funo pesquisa de modo que
uma advertncia seja escrita na tela quando o pedao de nome no for encontrado na lista de nomes. 

Bom estudo! 

Oswaldo Vernet
